秒懂网络编程核心概念

没有网卡的计算机之间不能通信(废话,但这表示0起点)

加上网卡以便通信

常见的两种网络:

  1. 以太网:使用以太网卡,用双绞线连接,低成本,适中速率,使用TCP/IP协议。常用
  2. 光纤网:使用光纤网卡,用光纤连接,高成本,高速率,使用SDH协议等。特定场景使用。本文不深入论述光纤网络。

在网卡已插入计算机,网卡之间通过网线连接后,可以理解路已经修好了。

另外,通信的各个网卡需要一个地址(这个是肯定的,就像寄快递,都要有地址),网卡地址(即mac地址)出厂时由厂家烧录,确保唯一但无规律。

路修好了还需要一种软件协议实现将消息从一台计算机发送到另一台计算机

IP(互联网协议)协议出现了,它主要做了两个工作:

  1. 优化了地址机制:由于物理网卡地址的无规律性,很多特性无法实现,因此IP协议在其之上定义了一套自己的地址机制,即IP地址。IP地址与mac地址相关联,最终的地址锁定都是需要mac地址的。IP地址提供了很好的识别性及规律性。实际上可以理解为互联网上的地址经过了两次转换:
    1. 第一次转换从mac地址到IP地址,这是在网络七层协议栈中完成的
    2. 第二次是从IP地址到域名,这是在应用层由本地数据库或者额外的DNS服务器软件完成的。这次转换的原因:
      1. 对于普通用户,IP地址没有语义性,不易记,域名则有了很好的语义;
      2. 由于某些原因,如DHCP动态地址分配技术,IP地址是可能会变的,而这种变动使用端是不知道的,域名给了使用端一个永远不变的地址标志;
  2. 实现了将消息(也就是packet,即分组报文,或者叫包,一种字节序列)从一台计算机尽力而为的发到另一台计算机:
    1. 尽力而为体现在:
      1. 不保证每个消息都能送达;
      2. 不保证消息收到的顺序与发送时一致;
      3. 可能会重复发送;
    2. 只负责将消息从计算机发送到计算机,而不是将消息从计算机上的某个程序发送到另一台计算机上的特定程序。由于消息收发都是程序与程序之间的事情,而不是计算机之间,所以如果只有IP协议实际上并不能真正实现消息收发。

现在是解决程序与程序之间如何通信的问题

  1. 每台计算机都有许多程序,这些程序都可能需要与另一台计算机上的程序通信;
  2. 程序是位于操作系统之上的,程序要通信必须要让操作系统为其打开一个口子以便与外面通信,这个口子即是端口。每个端口都有一个编号,俗称端口号,端口号从0开始到65535,其中0到1023之间的已被常见应用约定占用了,普通应用只能使用后面的端口号;显然的,一台计算机上不可能有超过6万多个程序要通信,所以端口号是够用的,即使如此,不恰当的编程仍然可能在特定情况下耗尽端口号。
  3. 操作系统可以开6万多个端口,日常生活中的插座板也就4个8个什么的,插座板上可以同时插上手机充电,同时插上电视插头看电视,这里的手机和电视就好像两个程序与外面的电网交换电流一样,因此第一次取名的人就用插座板(socket)这个名字来表示程序通过端口与外界通信;
  4. Socket如何翻译为中文呢?
    1. 如果直译的话,会是插座,插槽,或者窝,穴;这样的名字如果作为技术术语显然不合适了,插座编程听起来更像是搞人工智能插座开发。
    2. 或者就像“鲁棒性”一样,通过音译的方式,叫“索凯特”编程,听起来也很别扭。
    3. 再看看new Socket(ip,port),在这里ip和端口结合在一起作为一个地址,表示远程计算机上的一个进程,现在用Socket把这个地址住以便与远程进程接起来,然后就可以通过这个Socket实现与远程进程通信了,于是“套接”一词出现了,但套接是一个动词,用来表示一个事物显然不合适了,所以在后面加个字吧,于是“套接字”出现了。即使今天看来也难想出比套接字更好的翻译了。
  5. 在计算机中,插入网卡并安装网卡驱动之后还不能实现通信,由于IP协议是一种软件协议,它也是需要安装的,但是IP协议并不是单独提供的而是与TCP协议一起提供的,所以还需要在网卡上安装TCP/IP协议,但这个安装过程往往是系统自动完成的我们感觉不到它的安装过程。实际安装的一般是TCP/IPv4或者同时安装了TCP/IPv6;注意这里面已经包含了后面要讲的UDP协议了,只是名字上没有体现出来;
  6. 前面提到了IP协议的两个问题,我们继续看一下该怎么解决:
    1. 因为“尽力而为”带来的问题,我们考虑在IP协议之上再加一层软件协议,这个协议是用来控制消息传输的,确保消息可靠送达并且保证顺序,于是“传输控制协议”即TCP协议诞生了;
    2. 第二个就是要增加寻址功能,即实现将消息从某个端口发到IP层,IP层将消息传给目标计算机,目标计算机再分配给目标端口上的进程;
  7. TCP协议带来了好处,根据问题两面性规律,它也会带来一些问题:
    1. 消息是可靠了,但是这些保证机制是有成本的,它们适用于可靠性要求高的场景,但类似于视频播放等这种不要求十分可靠性的场景(比如掉一个一两个帧不影响观看),它的这种可靠性就属于费力不讨好了;
    2. 基于连接的面向流的通信,意味着已经锁定了通信的两端,这样就样就无法实现一端发送多端接收的效果,即广播效果了,而有些场景我们需要这种效果;
  8. 为了解决上面两个问题,UDP协议出现了,它也是基于IP协议之上的,但它不尝试对IP层产生的错误进行修复,仅增加了寻址功能。由于不保证消息送达所以它的send方法是不会阻塞的,send方法只把数据报扔到网络就返回,其余不管。UDP可以实现广播通信。
  9. 但我们前面讲安装协议的时候只看到TCP/IP啊?想想UDP与TCP使用场景的多少,以及我们是愿意用可靠的东西来作为命名主体还是不可靠的,就明白了。当然命名为TCP/UDP/IP也说得过去。
  10. 很明显了,套接字分TCP套接字与UDP套接字。而且在同一个计算机上,TCP和UDP套接字可以绑定到同一个端口号而不会产生端口冲突。

深入TCP套接字(UDP套接字以后再讲)

  1. 它是面向连接的协议,以流的方式通信;通信前需要交换握手消息以建立连接,结束时也要交换握手信息以关闭连接。
  2. 一旦建立好连接,会为程序提供两个流,输出流和输入流。输出流用于向网络写入数据,输入流用于从网络接收数据;
  3. 跟打电话一样,只要通信连接建立成功,客户端和服务器之间就没有区别了。
  4. 以输出流来看,由于向网络中写出的是字节流,发出10个消息但在接收方看来就是一串字节,接收方无法定位消息的边界,也就没有办法识别消息,这说明消息发送方和接收方需要对消息的边界进行协调界定,这个过程就叫“成帧”,即让消息是一帧一帧的有界可询。成帧的技术主要有两种:
    1. 基于定界符:在每发送完一个消息后,发一串约定的标识表示消息的结尾。这要求这串约定的标识不能出现在消息体中,但要知道消息体中是否出现了这一串,或者出现了之后对其进行转换处理,都需要对消息进行扫描,而扫描是有代价的。如果消息体中出现了定界符则需要使用“填充”技术对其加以处理。
    2. 基于长度的:即在消息头用一个字段指示消息体的长度;
  5. 将消息区分出一帧一帧之后,事情并没完,此时接收方看到的帧里面的内容是一串字节,它仍然无法解读。也就是是发送与接收方需要对消息内容格式进行规定以便互相能解读,这就是内容协议;
  6. 成帧协议与内容协议一起俗称应用层协议。在开发自己的客户端服务器软件时,应用层协议往往都是自定义的。但是实际生活中总是有一些可以场景抽象出来从而使用一种标准化的应用协议,HTTP协议便是这其中一种。HTTP协议是基于TCP套接字的应用层协议,有了HTTP协议,很多通用CS类型的应用就可以客户端与服务器端分别的蓬勃发展了,于是客户端出现各种浏览器,服务器端出现了各种web服务器实现,只要都遵守这个协议即可;
  7. HTTP协议虽然好,但是它只是请求响应模式,为了实现这个模式,它放弃了TCP全双工通信特性,不再支持客户端与服务器双向通信,直接地看就是服务器不能向客户端推送消息了。但是推送对于某些场景是很重要的,因此有必要在浏览器上实现访问socket这种接口以便实现双向通信,在web浏览器上访问socket简称websocket技术,这种特性需要web服务器和浏览器在原有的功能上增加websocket通信模块,因此老的浏览器可能不支持websocket通信;Websocket可以理解为也是一种TCP套接字。
  8. HTTP协议对于TCP连接是以短连接模式使用的,这意味着它并不适合自有的客户端服务器应用场景,这主要是因为性能问题。要知道连接的建立代价是很高的,想一下为什么数据库连接池组件为什么重要就明白了,代价这么高的东西用一下就关掉,省事是省事了,性能下去了,所以HTTP不适合面向服务架构内部组件通信使用。
  9. TCP套接字其它特性:
    1. 由于网络抖动、重试等各种因素作用,在TCP连接关闭后,网络链路上可能还残留有一些垃圾报文,由于是在链路上存在这些脏数据,两端的关闭操作都无法确保这些脏数据已经被彻底清理完毕了,假如在关闭后另一个程序马上又使用同一个端口通信,则它很可能接收到这种脏数据,为了应对这一情况,TCP协议规定,TCP连接在关闭后,必须至少有一段的TCP套接字保持TIME_WAIT一段时间以便让残留数据包能够有时间清理,根据实现不同,这个时间可能要数秒或者数分钟,也就是说TIME_WAIT状态下此端口是不可用的,如果某个应用宁愿冒着接收脏数据的风险马上要用这个端口,那它的TCP套接字在连接之前需要设置地址重用标记,否则就要等到TIME_WAIT状态消失之后才能用。
    2. Socket有参数的那个构造函数使用的是系统定义的连接超时,但是系统定义的超时时间很长,此时可以使用它的无参构造函数然后再调用connect方法以便指定自己的连接超时。
    3. 设置读取操作的超时时间:setSoTimeout()。如果不指定超时则表示永久等待。但对于写操作,没有提供超时机制,这是因为UDP的写操作本身就不阻塞,而TCP的写操作会阻塞,但是目前java语言还没有为其提供超时控制机制。
    4. TCP的keep-alive机制:反馈耗时太长,一般都是应用层自己做心跳机制而不用TCP的保活机制。
    5. 默认情况下TCP套接字在发送消息时,会将消息缓冲到一定数量再发给网络,这是为了优化网络传输,但这也会让消息发送产生延迟。在一些极端场景,如果不能接受这种延迟,可以通过TcpNoDelay设置为true来禁用这种缓冲机制从而避免这种延迟。
    6. 关闭TCP套接字会自动关闭其下面的两个流,关闭流时已经写出去的会让它发过去,如果希望确保关闭时所有数据已经发出,可以在调用close()方法前调用SoLinger方法设置close方法停留功能。此特性使用意义并不大。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值