Java网络编程第3版不完全详解

456 阅读20分钟

封面10.png

作者:老九—技术大黍

原文:Java Network Programming 3rd Edition

社交:知乎

公众号:老九学堂(新人有惊喜)

特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系笔者授权

前言

我们看到现在面试一个Java后端程序员,99%会被被问及计算机相关的底层原理,在网络方面一般都会问什么套结字啦,什么三次握手啦等... 当然,这个是面试,但是实际上Java程序员撸代码时根本没有这些直接关联的东西存在,因为都被Java的API深度封装了。

那么,作为一个纯Java程序员,也就是在“Java编程思路第4版-不完全详解” 一文中大牛Bruce. Eclkel所定义的“software craftsman”,要怎么学习才能掌握Java的网络开发呢?其实,我们认为只要阅读了《Java Network Programming 3rd Edition》的前8章就行了。

image-20210325115016338.png

因为,现在的Java后端开发99%都是使用Web服务器,包括现在最流行的Spring Cloud微架构应用都是默认内嵌套Tomcat或者Netty这种Web服务器(都是使用HTTP网络协议实现的)作为应用的微服实现载体。

这个与上世纪90年代以及21世纪初期的EJB重量级服务器(都是使用套结字技术实现的)大不相同,但是实际业务逻辑架构是不变的,只是实现这些服务的软件应用载体变了而已。因此,我们认为现在的Java程序员对网络真正认知达到这本书前8章讲的内容就行了。

好了,言归正传,下面内容是一个计科系出身的程序员翻译和解读的,请大家不要当成标准,如果有不足之处,请大家指正和补充。

网络概念

网络(network)是一种集合,该集合是计算机和其它外设的集合。而这些外设可以彼此之间实时的发送和接收数据。一般来说,网络是由金属线连接起来,然后把数据位转换成电子信号之后,通过这些金属线传送的。无线网络传送数据是通过红外线和微波技术来传输数据;而长距离的数据传输现在是通过光纤技术。但是这些物理媒体对于传输数据来说是没有差别的。理论上讲,数据可以被内燃机(coal-powered computers)车一样被传输,传输时是计算机之间通过使用发送“烟雾信号”(smoke signals)来交流的;而是响应时间要看网络环境了。

在网络上的每个机器被叫做一个结点(node)。大多数结点都计算机、打印机、路由器、桥接器、网关、终端,以及Coca-Cola销售终端等。我们可以使用Java接口Coke机器讲话,也可以与其它计算机讲话。具有独立完全功能的计算机结点被叫做主机(host)。我们可以使用单词结点(node)来表示在网络上的任何一台设备,而单词host(主机)表示网络上有特定目的结点计算机(general-purpose computer)。

每个网络结点都有一个地址(address),这个地址是唯一的字节序列。我们可以把这些字节序列看成一个数,但是它只是一个字节数,它们不能保证与Java的任何一个原始数字类型匹配。如果地址中可用的字节数越多,那么表示可以使用的地址越多,也就是可以连接到网络的设备越多。

地址被表示成不同的网络类型,AppleTalk地址每个主机启动时随机生成的。然后主机会检查网络中其它机器是否使用该地址。如果其它机器正在使用该地址,那么主机会选择其它的地址,然后又检查是否被使用过了,以此循环直到找玛个没有使用的地址,然后连接到网络中去。以太网(Ethernet)的地址是被附加到物理的以太网卡中的。每个以太网络的生产商会保证每个网卡不会有相同的地址。互联网地址是由计算机组织来确定的,而这种地址是由一个组织允许计算机通过ISP (Internet Service Provider)赋于的地址被使用。ISP的IP地址是四个区域性互联网注册机构ARIN (the registry for North America)之一授予的,ARIN负责把转换IPS赋于的IP地址,转换时使用ICANN (Internet Corporation for Assigned Names and Numbers)。

所有现代计算机网络都是packet-switched(包交换)网络;数据传输被切割成数据块(叫做packets)并且每个数据包是被分开处理的。每个包都包含有发送自身信息和自身被发送到哪里的信息。使用独立数据包的最大优势就是可以使用一条金属线来交换数据,这样可以使用构建网络的成本很低,我们甚至可以使用电话线来传输数据。 这里我们还漏掉了重要的信息:计算机需要说明传送什么数据。而协议(protocol)可以精确的定义交流的规则:

地址的格式,数据怎样被切割成数据包等。在网络中的计算机交流中定义不同的协议。比如HTTP协议,它定义了web服务器和浏览器交流格式等;协议的发布可以让软件与不同的设备提供商可以彼此交流;这样我们的浏览不用关心服务器是Unix工作站、Windows操作系统或者Macintosh服务器等。因为,服务器和浏览器都是使用HTTP协议的话,此时与平台无关。

image-20210325134620612.png

网络分层

跨网络发送数据是一个复杂的操作,需要我们小心的把发送数据的逻辑字符转换成物理字符来进行发送。跨网络发送数据的软件必须理解怎样避免接收数据冲突的数据包,然后转换数据信号为模拟信号,检查和校正错误,把数据包从一个主机路由到另外的主机等。它的复杂度甚至比多个操作系统和异种网络线路更复杂。

要完成这种复杂的管理和隐藏复杂管理动作,让开发人和用户使用,那么需要把网络通信分成从不同的方面来看待它。每一层表示了不同的抽象,这种抽象是物理设备与被传输信息之间的抽象。每一种被限定了功能。比如,一层负责响应路由的数据包,而另外一层负责发现和请求丢失的数据包。理论上每一层只能与直接上一层和直接下一层讲话。网络分层的结果就是可以让我们的软件放在同一层,而不需要影响其它层。换句话说,层与层之间有相同的接口。

根据特定的网络类型有不同的分层模型,这里讲的是使用TCP/IP四层模式,它对于于Internet网络。

image-20210325134735529.png

  • The layers of a network--网络分层(这里是实现四层模型,理论模型是七层)
  • Application Layer--应用层
  • Transport Layer(TCP,UDP)--传输层(TCP和UDP协议)
  • Internet Layer(IP)--互联网层(IP协议)
  • The Host-To-Network Layer(Ethernet, localTalk, FDDI, etc)--主机到网络层
  • logical path--逻辑路径
  • physical path--物理路径

在这个模型中,IE浏览器和Eudora是处于应用层的,它们只能与传输层讲话。而传输层只能与应用层和IP层讲话。而IP层只能与物理层讲话,物理层不能直接对传输层和应用层讲话。比如,当一个Web浏览器发送一个请求给一个Web服务器想得到一个网页时,该浏览器实际上是只能与本地机器上的传输层说话。然后传输层把该请求分解为TCP段,添加一些序列号和检验码到数据中去,然后把该请求传输到本地Internet层(IP层);接着IP层添加IP数据包给本地网络,让本地网络层(host-to-network layer)把它们通过网线发送到目的。

远程的主机物理层接收到数据包之后,把模拟信号还原成数据信息,然后传送到IP层,IP层做相应的检查数据包是否损毁,然后重装数据包之后把它们传送到传输层;在服务领导人传输层会检查所有的数据是否达到,并且重传丢失的数据。一旦服务器的传输层接收到足够的数据,那么它会把数据拆解出来,然后写成流对象中去,最后通过Web服务器的应用层来读取流对象。接着web服务器响应浏览器时与浏览发送的过程相同。

实际处理时在物理层会复杂,但是90%的Java代码只需要在应用层工作,所以只需要与传输层说话。而只有10%的时间需要Java代码处于传输层中,然后与应用层或者IP层讲话。而复杂的物理层被封装起来了。Java程序员不需要看到物理层。

image-20210325135157500.png

  • The Structure of an IPv4 datagram--IPV4报文结构
  • version--版本号
  • header length--头的长度
  • type of service--服务类型
  • datagram length--报文的长度
  • identification--唯一标识
  • flags--标记
  • fragment offset--断偏移量
  • time-to-live(TTL)--时间到生存期
  • protocol--协议
  • header checksum--头部校验
  • source address--源地址
  • destination address--目标地址
  • options--可选项
  • data--数据内容本身

当然,现在已经是IPV6标准了,我们熟练IPV4标准结构,然后理解IPV6标准的结构也不会太难。

IP、TCP和UDP协议

IP(互联网协议)相比于AppleTalk和IPX协议有更大的优势。它的由起是来自冷战(Cold War)期间美国军方的赞助而开发出来。它的许多特点是军方感兴趣的。该协议第一个特点是健壮性(robust),比如,如果前苏联核袭击了Cleveland州,那么整个网络不会瘫痪。因为,IP允许两个结点之间的数据包可以多个路由来实现。第二,军方有很多不同类型的计算机,而IP协议可以让它们之间彼此通话。所以,IP被开放出来,并且是平台独立的。TCP是基于IP层之上,提供了可靠连接能力来进行发送和接收IP数据包;而UDP是基于IP层提供了非可数据包传送能力。UDP的速度快于TCP协议传输,而TCP提供可靠数据包传输协议;Java只支持TCP和UDP协议传输。

image-20210325140155798.png

  • echo高阶协议--它的端口7,使用低阶TCP/UDP协议,它是一种测试协议,用来测试两台计算机之间是否可以连接。
  • discard高阶协议--它的端口9,使用低阶TCP/UDP协议,它是一种很少使用的测试协议,它允许服务器把接收的数据忽略掉。
  • daytime高阶协议--它的端口13,使用低阶TCP/UDP协议,它提供ASCII表示当前的服务器时间。
  • FTP data高阶协议--它的端口20,使用低阶TCP协议,它是大家都的端口,用来传输文件时使用。
  • FTP高阶协议--它的端口21,使用低阶TCP协议,它用一发送FTP命令,比如put和getw命令。
  • SSH高阶协议--它的端口22,使用低阶TCP协议,它用是加密和远程登录时使用的。
  • telnet高阶协议--它的端口23,使用低阶TCP协议,它用来与远程服务端交互的,它是一个远程命令会话。
  • smtp高阶协议--它的端口25,使用低阶TCP协议,简单邮件协议,用来实现两个计算机之间发送电子邮件的。
  • time高阶协议--它的端口37,使用低阶TCP/UDP协议,它是一个时间服务器,返回时间的秒数。时间从1900年1月1日开始计算。
  • whois高阶协议--它的端口43,使用低阶TCP协议,它是一个简单目录服务,这种服务是提供给互联网管理员使用的。
  • finger高阶协议--它的端口79,使用低阶TCP协议,它是本地系统的用户的信息的服务。
  • HTTP高阶协议--它的端口80,使用低阶TCP协议,它是WWW万维网的底层协议。
  • POP3高阶协议--它的端口110,使用低阶TCP协议,它是邮件协议3版本,它是用来转换收到电子邮件到一个指定的客户端。
  • NNTP高阶协议--它的端口119,使用低阶TCP协议,它是用户新闻传输,有点像“网络新闻传输协议”。
  • IMAP高阶协议--它的端口143,使用低阶TCP协议,互联网消息访问协议,用来访问服务串的邮箱的。
  • RMI Registry高阶协议--它的端口1099,使用低阶TCP协议,用来注册Java远程对象的。

关于什么是高阶协议和低阶协议的概念,网上吹牛批的一大堆,请大家自行百度参考理解,我们在这里不再累述了。

互联网

互联网(Internet)是世界上最大的基于IP协议的网络。它一个无定式的计算机组,这些计算机在不同的国家甚至七大洲(包括南极洲),它们都可以使用IP协议来进行讲话。在互联网上的每台计算机,至少有一个唯一的IP地址被定义。大多数这些计算机有一个名字来影射IP地址。互联网不属于任何人,它也不能被任何人所管理—不是政府不可以去管理。它只是一个非常简单的超大计算机的集合!该集合会基于一种标准来进行彼此讲话和交流。

为避免冲突,IPV4地址被分为A、B、C、D、E五种,常用为三种C类可以表示254台主机;B类可以表示65024台主机;A类可以表示一千六百万台主机。

客户端/服务端模型(The Client/Server Model)

现代网络编程是基于客户端/服务器模型。一个C/S应用一般把数据存贮在昂贵、高效的服务器中,而大多数的程序逻辑和用户接口处理是由客户端软件来处理,并且运行在便宜的PC中上。大多数情况下是服务器发送数据,同时客户端接收数据。

image-20210325142452465.png

  • A client/server connection--客户端/服务端连接
  • Server--服务器(包含硬件和软件)
  • Client--客户端(包含硬件和软件)
  • Port--端口
  • Input Stream--输入流
  • Output Stream--输出流

2004年在互联网上最流行是C/S系统就是Web。而Web服务器像Apache响应web浏览器比如Fireofox的请求。数据是存贮在web服务器的,然后由web服务器发送回请求的浏览器。就客户端请求的网页来说,其实所有的数据都由服务器传送到浏览器的,而不是由浏览器客户端传送到服务器。Web服务器可以用于GUI编程,同时也可以作为应用和文件服务器。FTP是一种比较老的C/S模型的服务架构,只是FTP使用不同的协议和不同的软件来让FTP客户端向FTP服务器端发送文件。人们一般使用FTP上传文件到服务器,或者下载文件。但是,不是所有的应用都适应于C/S模型,比如网络游戏,它需要两个玩家之间进行数据传送,这种连接叫P2P (peer-to-peer)连接方式。

这种模型参见Sun公司的JXTA项目组的P2P网络编程框架。不过在服务器端很容易提供P2P的交互方式,这种情况下我们可以让服务器即作为服务器端应用,同时也可以具有客户端功能。另外,peer之间通信可以通过一个中介服务器程序把数据从一个peer传送到其它的peers.这种传奇特别适合Applet这种基于安全管理的网络编程。

image-20210325142715078.png

基本的Web概念

Java可以许多浮华的网页,但是,我们大多数的程序是在网页中的applet小程序,而servlet运行在服务端,或者web server需要与其它的web服务器和客户端讲话。而这所有一切,都必须对web服务器与web客户端之间的交互有一个非常深刻的理解。

HTTP (Hypertext Transfer Protocol)是定义web客户端与web服务器之间交互的标准。这个标准定义了客户端怎样与服务器交互,数据是怎样从服务器传送到客户端的。定义HTTP协议的框架是REST (Representational State Transfer). HTTP可以被用来传送任意格式的数据,从TIFF图片到MS的Word文档和Dbase文件。但是,大多数在web网上传送的通用数据是使用web的固有格式语义

HTML (Hypertext Markup Language)来传输。HTML是一种简单标准,该标准定义文本数据的语义的标准。我们可以使用它来描述“this is a header”, “this is a list item”等,但是我们不能使用它来描述header和item到底是什么样子的:它们长得什么样是由浏览来决定的!HTML只所以是超文本标记语言,是因为它指定一种方式来连接到其它的文本,这种方式就是URL。一个URL就是任何定位互联上确定的资源的方式。要理解网络编程,那么我们必须理解URL、HTML和HTTP概念。

URI

URI (Uniform Resource Identifier)是一个字符串,这个字符串有特定的语义格式,该格式用来表示一个资源。这些资源可以是服务器上的一个文件,也可以email地址、一个新闻、一本书、一个的姓名、一个互联网上的主机名、当前sun公司的股票等等。URI由scheme和scheme-specific中间加一个冒号组成:

image-20210325142910615.png

scheme可以是data—基于Base64编码的数据,包括一个连接中的内容; 一个本地磁盘上的文件;一个FTP服务器;一个HTTP协议;一个gopher服务器;一个email地址;一个新闻组;一个telnet连接服务;一个urn(统一资源名称)。

在URI的scheme-specific部分没有特定的语义格式,比如:

//authority/path?query

其中authority部分是授权响应剩下的部分比如URI这样描述:www.ietf.org/rfc/rfc2396…. 然后authority是www.ietf.org字符串。表示服务器是www.ietf.org.它负责影射资源/rfc/rfc2396.txt资源。该uri没有查询部分。

路径(包括最初始的”/”符号)也是一个字符串,该字符串是由authority可以确定资源的字符串。比如/index.html。

scheme部分可以由小写字母、数字和加号、点号和连字符组成。其它三个部分(authority, path and query)可以由ASCII码字母组成:A-Za-z0-9字样。另外还可以使用- _ . ! ~ * ‘等字符。á会被表示与%E1字样。

HTTP

HTTP是一种通信标准,该标准定义了web浏览器与web服务器之间通信的标准。HTTP定义一个客户端和服务端怎样建立连接,客户端怎样从服务端请求数据,服务端怎样响应客户端的请求,最后怎样关闭连接。HTTP连接是使用TCP/IP的数据传输协议的。每个客户端向服务器发送一个请求必须有以下四个步骤:

  • 建立连接—客户端默认在服务器的80端建立TCP连接,其它端口可以由URL来指定。

  • 建立请求—客户端使用特定的URL来向服务端发送消息。标准格式如下:GET /index.html HTTP/1.0,其中GET指定是请求的操作类型。它要求服务端返回相对路径的/index.html资源。HTTP/1.0表示协议的版本。请求使用两个回车表示请求结束。其中,我们需要知道GET请求行是必须的,客户端请求还可以包含其它的请求信息。这些信息使用keyword:Value格式。

  • Accept—表示服务器哪些数据客户端可以处理,比如MIME媒体类型,JPEG和GIF图片。

    Accept: text/html, text/plain, image/gif, image/jpeg

  • User-Agent—是让服务器知道哪类浏览器正在向服务器发送请求。

    User-Agent: Lynx/2.4 libwww/2.1.4

  • Host:www.cafeaulait.org

    最后两个回车表示结束请求。

除了GET操作之外,还有POST请求把数据发送到服务器,PUT操作是上传资源到服务器;DELETE操作是把服务中的资源删除。完成的请求示例如下:

image-20210325143517652.png

然后是服务器响应,最后是关闭连接:浏览器与web服务器都要关闭连接。如果客户端重新连接,那么在服务器端的内容中没有保存以前连接过的信息,这种协议不保存请求的信息叫无状态协议。相反,FTP是有状态协议,而这种无状态是HTTP优点与是它的缺点。

下面是服务器的响应代码:

image-20210325143618827.png

image-20210325143635119.png

演示代码

import static java.lang.System.*;
import java.net.*;
import java.io.*;

/**
	功能:模拟HTTP的表单发送POST请求
	*/
public class FormPoster {

  private URL url;
  private QueryString query = new QueryString(); 
   
  public FormPoster (URL url) {
    if (!url.getProtocol().toLowerCase().startsWith("http")) {
      throw new IllegalArgumentException(
       "Posting only works for http URLs");   
    }    
    this.url = url;
  }
  
  public void add(String name, String value) {
    query.add(name, value);    
  }
  
  public URL getURL() {
    return this.url; 
  }

  public InputStream post() throws IOException {

    // open the connection and prepare it to POST
    URLConnection uc = url.openConnection();
    uc.setDoOutput(true);
    OutputStreamWriter out 
     = new OutputStreamWriter(uc.getOutputStream(), "ASCII");
   
    // The POST line, the Content-type header,
    // and the Content-length headers are sent by the URLConnection.
    // We just need to send the data
    out.write(query.toString());
    out.write("\r\n");
    out.flush();
    out.close();


    // Return the response
     return uc.getInputStream();

  }

  public static void main(String args[]) {

    URL url;

    if (args.length > 0) {
      try {
        url = new URL(args[0]);
      }
      catch (MalformedURLException ex) {
        System.err.println("Usage: java FormPoster url");
        return;
      }
    }
    else {
      try {
        url = new URL(
          "http://my.51job.com/my/My_SignIn.php?url=%2Fmy%2FMy_Pmc.php%3F0157");//访问51job网站
      }
      catch (MalformedURLException ex) { // shouldn't happen
        System.err.println(ex);
        return; 
      }
    }

    FormPoster poster = new FormPoster(url);
    poster.add("userpwd", "0000000"); //加自己的账号和密码
    try {
      InputStream in = poster.post();
    
      // 读取服务端的响应的内容
      InputStreamReader r = new InputStreamReader(in);
      int c;
      //写到文件方便查看web服务器响应的内容
      FileOutputStream write = new FileOutputStream(new File("test.html"));
      while((c = r.read()) != -1) {
        System.out.print((char) c);
        write.write((char) c);
      }
      System.out.println();
      in.close();
    }
    catch (IOException ex) {
      System.err.println(ex);   
    }
  }
}

总结

我们觉得,纯Java程序员只要把协议搞懂,以及理解了客户端/服务端的互联网网络连接模型,那么在实际开发中就是使用各种流对象操作网站就行了。并且,当我们使用Servlet/JSP技术时,连上面的代码都摸底给我们封装了,但是只要我们能够理解Tomcat服务器帮我们做哪些纯Java网络应用应该做的事情,那么,大家要做一个Java网络开发人员是非常轻松、愉快的事情。

我们希望通过这篇文章的抛砖引玉,能够给大家在学习和使用Java开发网络应用带来帮助。如果不足之处,请大家指正和补充。

最后

记得给大黍❤️关注+点赞+收藏+评论+转发❤️

作者:老九学堂—技术大黍

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。