作者:老九—技术大黍
原文:Java Network Programming 3rd Edition
社交:知乎
公众号:老九学堂(新人有惊喜)
特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系笔者授权
前言
我们看到现在面试一个Java后端程序员,99%会被被问及计算机相关的底层原理,在网络方面一般都会问什么套结字啦,什么三次握手啦等... 当然,这个是面试,但是实际上Java程序员撸代码时根本没有这些直接关联的东西存在,因为都被Java的API深度封装了。
那么,作为一个纯Java程序员,也就是在“Java编程思路第4版-不完全详解” 一文中大牛Bruce. Eclkel所定义的“software craftsman”,要怎么学习才能掌握Java的网络开发呢?其实,我们认为只要阅读了《Java Network Programming 3rd Edition》的前8章就行了。
因为,现在的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协议的话,此时与平台无关。
网络分层
跨网络发送数据是一个复杂的操作,需要我们小心的把发送数据的逻辑字符转换成物理字符来进行发送。跨网络发送数据的软件必须理解怎样避免接收数据冲突的数据包,然后转换数据信号为模拟信号,检查和校正错误,把数据包从一个主机路由到另外的主机等。它的复杂度甚至比多个操作系统和异种网络线路更复杂。
要完成这种复杂的管理和隐藏复杂管理动作,让开发人和用户使用,那么需要把网络通信分成从不同的方面来看待它。每一层表示了不同的抽象,这种抽象是物理设备与被传输信息之间的抽象。每一种被限定了功能。比如,一层负责响应路由的数据包,而另外一层负责发现和请求丢失的数据包。理论上每一层只能与直接上一层和直接下一层讲话。网络分层的结果就是可以让我们的软件放在同一层,而不需要影响其它层。换句话说,层与层之间有相同的接口。
根据特定的网络类型有不同的分层模型,这里讲的是使用TCP/IP四层模式,它对于于Internet网络。
- 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程序员不需要看到物理层。
- 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协议传输。
- 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中上。大多数情况下是服务器发送数据,同时客户端接收数据。
- 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这种基于安全管理的网络编程。
基本的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中间加一个冒号组成:
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操作是把服务中的资源删除。完成的请求示例如下:
然后是服务器响应,最后是关闭连接:浏览器与web服务器都要关闭连接。如果客户端重新连接,那么在服务器端的内容中没有保存以前连接过的信息,这种协议不保存请求的信息叫无状态协议。相反,FTP是有状态协议,而这种无状态是HTTP优点与是它的缺点。
下面是服务器的响应代码:
演示代码
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开发网络应用带来帮助。如果不足之处,请大家指正和补充。
最后
记得给大黍❤️关注+点赞+收藏+评论+转发❤️
作者:老九学堂—技术大黍
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。